import { jobGoto } from "../../../../job/goto";
import { getMaxEnergy, getMinEnergy, spawnCreep } from "../../../../role/spawn";
import { Group } from "../../../group";
import { findRoomHostiles } from "../../../../global/map";
import { jobSpread } from "../../../../job/spread";
import { canMoveOn, step } from "../../../../util/position";

export class GroupAttacker extends Group{
    constructor(spawnRoom = undefined, targetRoomName = undefined){
        super(spawnRoom);
        if(targetRoomName !== undefined){
            this.targetRoomName = targetRoomName;
            this.spawning = true;
        }
    }
    serialize(){
        return {
            super: super.serialize(),
            targetRoomName: this.targetRoomName,
            spawning: this.spawning,
        };
    }

    deserialize(obj){
        super.deserialize(obj.super);
        this.targetRoomName = obj.targetRoomName;
        this.spawning = obj.spawning;
        return this;
    }

    get targetRoom(){
        return Game.rooms[this.targetRoomName];
    }
    /**
     * 
     * @returns {(AnyCreep|AnyStructure)[]}
     */
    get targets(){
        if(this._targets !== undefined){
            return this._targets;
        }
        if(this.targetRoom && this.targetRoom._offenderTargets !== undefined){
            return this._targets = this.targetRoom._offenderTargets;
        }

        let hostiles;
        if(this.targetRoom instanceof Room){
            hostiles = findRoomHostiles(this.targetRoom);
        }else{
            hostiles = [{pos:new RoomPosition(24,24,this.targetRoomName)}];
        }
        if(this.targetRoom){
            this.targetRoom._offenderTargets = hostiles;
        }
        return this._targets = hostiles;
    }
    get creeps(){
        return [];
    }
    get maxSize(){
        return 0;
    }
    get size(){
        return 0;
    }
    isSpawning(){
        if(!this.spawning){
            return false;
        }
        for(let creep of this.creeps){
            if(creep.spawning){
                return true;
            }
            if(creep.ticksToLive<1400){
                return true;
            }
        }
        if(this.maxSize <= this.size){
            this.spawning = false;
        }
        return this.spawning;
    }

    static get spawnTemplate(){
        return {
            ranged_attacker: {
                require: [MOVE, RANGED_ATTACK],
                move: .34,
                ranged_attack: .66,//todo: don't use whole capacity
            },
            attacker: {
                require: [MOVE, ATTACK],
                move: .34,
                attack: .66,
            },
            healer: {
                require: [MOVE, HEAL],
                move: .34,
                heal: .66,
            },
        };
    }

    /**
     * 
     * @param {"ranged_attacker"|"attacker"|"healer"} templateName 
     * @param {(AnyCreep)=>void} successCallback 
     * @returns {CreepActionReturnCode|Creep}
     */
    spawnTemplate(templateName, successCallback){
        if(!GroupAttacker.spawnTemplate[templateName]){
            return ERR_INVALID_ARGS;
        }

        let newCreep = spawnCreep(this.spawnRoom, 
            GroupAttacker.spawnTemplate[templateName],
            {
                memory: {
                    role: templateName,
                },
            }, getMaxEnergy(this.spawnRoom));
        if(!(newCreep instanceof Creep)){
            return newCreep;
        }
        successCallback(newCreep);
        return OK;
    }
    /**
     * 
     * @param {AnyCreep} creep 
     * @returns {CreepActionReturnCode}
     */
    renew(creep){
        if(creep.ticksToLive>1500-Math.floor(600/creep.body.length)){
            jobSpread.run(creep);
            return OK;
        }
        let spawns = this.spawnRoom.find(FIND_MY_SPAWNS,{
            filter(spawn){
                return !spawn.spawning;
            }
        });
        if(spawns.length === 0){
            jobSpread.run(creep);
            return ERR_BUSY;
        }
        let nearest = jobGoto.getClosest(creep, spawns);
        let ret;
        if(!nearest.pos.isNearTo(creep.pos)){
            ret = jobGoto.run(creep, [nearest]);
        }else{
            ret = nearest.renewCreep(creep);
        }
        if(ret !== OK && ret !== ERR_TIRED && ret !== ERR_NOT_IN_RANGE){
            jobSpread.run(creep);
        }
        creep.say("renew:"+ret);
        return ret;
    }

    run(){
        if(this.isSpawning()){
            for(let creep of this.creeps){
                this.renew(creep);
            }
            return OK;
        }
        return this.clusterRun();
    }

    /**
     * 
     * @param {RoomPosition} pos 
     * @param {DirectionConstant} direction 
     * @returns {RoomPosition}
     */
    step(pos, direction){
        return step(pos, direction);
    }
    /**
     * 
     * @param {RoomPosition} pos 
     */
    canMoveOn(pos){
        return canMoveOn(pos);
    }

    /**
     * 
     * @param {AnyCreep} creep 
     * @param {AnyCreep} target 
     */
    creepFollow(creep, target){
        if(creep.pos.isNearTo(target)){
            console.log('asdf')
            return creep.move(target);
        }
        let distance = creep.pos.getRangeTo(target);
        // if(distance < 10){
        //     console.log('fdsa')
        //     return creep.move(creep.pos.getDirectionTo(target));
        // }
        return creep.moveTo(target, {reusePath: Math.min(distance-2,0)});
    }

    /**
     * 
     * @param {DirectionConstant} direction 
     * @param  {...AnyCreep} creeps 
     * @returns {CreepActionReturnCode}
     */
    move(direction, ...creeps){
        if(!([1,2,3,4,5,6,7,8].some((dir)=>dir === direction))){
            return ERR_INVALID_ARGS;
        }
        for(let creep of creeps){
            if(!this.canMoveOn(this.step(creep.pos, direction))){
                return ERR_NO_PATH;
            }
            if(creep.fatigue>0){
                return ERR_TIRED;
            }
            if(!creep.my){
                return ERR_NOT_OWNER;
            }
            if(creep.spawning){
                return ERR_BUSY;
            }
            if(creep.getActiveBodyparts(MOVE) === 0){
                return ERR_NO_BODYPART;
            }
        }
        
        for(let creep of creeps){
            creep.move(direction);
        }
        return OK;
    }
    /**
     * 
     * @overload
     * @param {RoomPosition} target 
     * @param {...AnyCreep} creeps 
     */
    moveTo(target, ...creeps){
        let head;
        for(let creep of creeps){
            if(!creep){
                continue;
            }
            if(!head){
                creep = head;
                continue;
            }
            this.creepFollow(creep, head);
        }
        let ret = this.creepFollow(head, target);
        if(ret === ERR_TIRED){
            ret = OK;
        }
        return ret;
    }
    /**
     * 
     * @abstract
     */
    clusterRun(){}
} 

export function getOppositeDirection(direction){
    return (((direction-1)+4)&0b111)+1;
}